package cn.jofei.common.monitor.msg;

import cn.jofei.common.config.AppProperties;
import cn.jofei.common.enums.Headers;
import cn.jofei.common.enums.MediaType;
import cn.jofei.common.utils.Streams;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

/**
 * 钉钉消息发送
 *
 * @author Jofei Bao
 * @version ngbao v2.0
 * @date 2022年07月15日 22:44
 */
@Data
public class DingMsg {

    private static final String dingUrl = AppProperties.get("monitor.ding.url");

    private static final String accessToken = AppProperties.get("monitor.ding.access-token");

    private static final String secretKey = AppProperties.get("monitor.ding.secret-key");

    private static final Logger LOGGER = LoggerFactory.getLogger(DingMsg.class);

    /**
     * markdown ,text
     */
    private String msgtype;

    private String text;

    private Markdown markdown = new Markdown();

    private At at = new At();

    public static MsgBuilder markdown(String title) {
        MsgBuilder markdown = new MsgBuilder("markdown");
        return markdown.title(title);
    }

    public static MsgBuilder text() {
        return new MsgBuilder("text");
    }

    public static class MsgBuilder {

        private String msgtype;

        public MsgBuilder(String msgtype) {
            this.msgtype = msgtype;
        }

        private DingMsg dingMsg = new DingMsg();

        public MsgBuilder text(String text) {
            if ("markdown".equals(msgtype)) {
                dingMsg.markdown.text.append(text);
                dingMsg.msgtype = "markdown";
            } else if ("text".equals(msgtype)) {
                dingMsg.msgtype = "text";
                dingMsg.text = text;
            }
            return this;
        }

        public MsgBuilder h1(String text) {
            dingMsg.msgtype = "markdown";
            dingMsg.markdown.text.append("# ").append(text).append("\n");
            return this;
        }

        public MsgBuilder h2(String text) {
            dingMsg.msgtype = "markdown";
            dingMsg.markdown.text.append("## ").append(text).append("\n");
            return this;
        }

        public MsgBuilder h3(String text) {
            dingMsg.msgtype = "markdown";
            dingMsg.markdown.text.append("### ").append(text).append("\n");
            return this;
        }
        public MsgBuilder h4(String text) {
            dingMsg.msgtype = "markdown";
            dingMsg.markdown.text.append("#### ").append(text).append("\n");
            return this;
        }
        public MsgBuilder h5(String text) {
            dingMsg.msgtype = "markdown";
            dingMsg.markdown.text.append("##### ").append(text).append("\n");
            return this;
        }

        /**
         * 必须添加
         *
         * @param title
         * @return
         */
        private MsgBuilder title(String title) {
            dingMsg.markdown.title = title;
            return this;
        }

        public MsgBuilder atUser(String user) {
            dingMsg.at.addUser(user);
            return this;
        }

        public MsgBuilder atMobile(String mobile) {
            dingMsg.at.addMobile(mobile);
            return this;
        }

        public DingMsg build() {
            return dingMsg;
        }

    }

    @Data
    class Markdown {
        private String title;
        private StringBuilder text = new StringBuilder();

        /**
         * json 转换时 StringBuilder 会变成 {}
         * @return string
         */
        public String getText() {
            return text.toString();
        }
    }

    @Data
    class At {
        List<String> atMobiles = new ArrayList<>();
        List<String> atUserIds = new ArrayList<>();
        Boolean isAtAll;

        public void addUser(String user) {
            atUserIds.add(user);
        }

        public void addMobile(String mobile) {
            atMobiles.add(mobile);
        }
    }

    private String sign(Long timestamp, String appSecret) {
        try {
            String stringToSign = timestamp + "\n" + appSecret;
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
            byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
            return new String(Base64.getEncoder().encode(signData));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void send() {
        String result = post(JSON.toJSONString(this));
        JSONObject parseObject = JSON.parseObject(result);
        Integer errcode = parseObject.getObject("errcode", Integer.class);
        if (errcode == null || errcode != 0) {
            LOGGER.error("发送钉钉消息失败，{}", parseObject);
        }
    }

    private String post(String param) {
        PrintWriter out = null;
        InputStream in = null;
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        try {
            long timestamp = System.currentTimeMillis();
            //签名信息
            String sign = sign(timestamp, secretKey);
            String url = dingUrl +
                    "?access_token=" + accessToken +
                    "&timestamp=" + timestamp +
                    "&sign=" + sign;
            URL realUrl = new URL(url);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty(Headers.CONTENT_TYPE, MediaType.JSON);
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = conn.getInputStream();
            byte[] buf = new byte[2048];
            int len = 0;
            while ((len = in.read(buf)) != -1) {
                result.write(buf, 0, len);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            Streams.safeClose(out);
            Streams.safeClose(in);
            Streams.safeClose(result);
        }
        return result.toString();
    }
}
